package xyz.thoughtset.viewer.common.core.factory;

import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import xyz.thoughtset.viewer.common.annotation.core.AMR;
import xyz.thoughtset.viewer.common.annotation.core.SubjectInfo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class AnnMgtFactory {
    @Value("${scan.package}")
    private String scanPackage;

    @Autowired
    protected ApplicationContext applicationContext;

    private Map<String, Class> SubjectMap = new HashMap<>();
    private static ConcurrentHashMap<Class, ConcurrentHashMap<Class,Annotation>> ObjAnnMap = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<Class, ConcurrentHashMap<Class,Annotation>> FunAnnMap = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        // 创建扫描器，false 表示不检查父类
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(AMR.class));
        // 扫描指定包
        Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(scanPackage);
        for (BeanDefinition beanDefinition : beanDefinitions) {
            try {
                Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
                String keyStr = null;
                SubjectInfo si = clazz.getAnnotation(SubjectInfo.class);
                if (si != null){
                    keyStr = si.value();
                }
                if (!StringUtils.hasText(keyStr)){
                    keyStr = StringUtils.uncapitalize(clazz.getSimpleName());
                }
                SubjectMap.put(keyStr,clazz);
                loadAllObjAnn(clazz);
                loadAllMethodAnn(clazz);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean hasObjExist(String key,Class annType){
        return hasObjExist(SubjectMap.get(key),annType);
    }
    public boolean hasObjExist(Class dataType,Class annType){
        return ObjAnnMap.containsKey(dataType) && ObjAnnMap.get(dataType).containsKey(annType);
    }

    public Class findTypeBySub(String dataType){
        return SubjectMap.get(dataType);
    }

    private synchronized static void loadAllObjAnn(Class clazz) {
        ConcurrentHashMap<Class,Annotation> result = new ConcurrentHashMap<>();
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            Class<? extends Annotation> annotationType = annotation.annotationType();
            if (AnnotationUtils.findAnnotation(annotationType, AMR.class) != null) {
                result.put(annotationType, clazz.getAnnotation(annotationType));
            }
        }
        ObjAnnMap.put(clazz, result);
    }
    private synchronized static void loadAllMethodAnn(Class clazz) {
        ConcurrentHashMap<Class,Annotation> result = new ConcurrentHashMap<>();

        // 遍历类所有方法
        for (Method method : clazz.getDeclaredMethods()) {
            // 获取方法上的所有注解
            Annotation[] annotations = method.getAnnotations();

            for (Annotation annotation : annotations) {
                Class<? extends Annotation> annotationType = annotation.annotationType();

                // 判断该注解类型是否被@a注解标注
                AMR ann = AnnotationUtils.findAnnotation(annotationType, AMR.class);
                if (ann != null) {
                    result.put(ann.getClass(),ann);
                }
            }
        }
        FunAnnMap.put(clazz, result);
    }

}
